function generatorToAsync(generatorFn) {
    return function () {
        // generatorFn 函数可能需要传参
        const gen = generatorFn.apply(this, arguments)

        // 返回一个 Promise 对象
        return new Promise((resolve, reject) => {
            const go = (key, arg) => {
                let res

                try {
                    if (key === 'next') {
                        /**
                         * 1.next 执行过程有异常被抛出，进入 catch
                         * 2.next 执行过程无异常，但是返回一个立即 reject 的 Promise 对象，进入 catch
                         * 3.next 执行过程无异常，返回一个立即 resolve 或 pending 状态的 Promise 对象，不进入 catch
                         */
                        res = gen.next(arg)
                    } else if (key === 'throw') {
                        /**
                         * 上一个 next 返回的 pending 状态的 Promise 对象状态变更为 rejected 后在此处处理
                         */
                        throw arg
                    }
                } catch (err) {
                    // 变更最终 Promise 的状态，且终止向下运行
                    return reject(err)
                }

                const { value, done } = res

                if (done) {
                    // done 为 true，说明执行完毕，进行 resolve
                    return resolve(value)
                } else {
                    // done 为 false，说明执行未完毕，继续
                    /**
                     * 这里的 value 可能是 Promise 对象也可能是其它类型，所以需要用 Promise.resolve 包裹一层
                     * 如果是其它类型，则会返回一个 fulfilled 状态，PromiseResult 为 value 的 Promise 对象
                     * 如果是 Promise 对象，value 对应的 Promise 对象会决定当前 Promise 对象的状态（见 Promise 精读系列）
                     */
                    return Promise.resolve(value).then(val => {
                        go('next', val)
                    }).catch(err => {
                        go('throw', err)
                    })
                }
            }

            // 第一次执行
            go('next')
        })
    }
}

function fn(num) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(num * 2)
        }, 1000)
    })
}

function* gen(num) {
    const num1 = yield fn(num)
    console.log(num1)
    const num2 = yield fn(num)
    console.log(num2)
    const num3 = yield fn(num)
    console.log(num3)
    return num3
}

const asyncFn = generatorToAsync(gen)

console.log(asyncFn(2))
